import Message from '@/components/antds/Message';
import { formatByte } from '@/utils/FormatUtil';
import { UploadOutlined } from '@ant-design/icons';
import type { UploadProps } from 'antd';
import { Button, Upload } from 'antd';
import type { UploadChangeParam } from 'antd/lib/upload';
import type { RcFile, UploadFile } from 'antd/lib/upload/interface';
import classNames from 'classnames';
import type { ReactElement } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import type { IDirection } from './UploaderConfig';
import { DefFilterAccept } from './UploaderConfig';
import type { IHistoryFiles } from './UploaderHistory';
import { UploaderHistory } from './UploaderHistory';
import { UploaderListItem } from './UploaderListItem';
import { UploaderPreview } from './UploaderPreview';
import { UploaderRender } from './UploaderStyle';
import type { ITipsGroupItem } from './UploaderTips';
import { UploaderTips } from './UploaderTips';
import {
  checkImageSize,
  checkIsImage,
  getFileBase64,
  getFileExt,
} from './UploaderUtil';

/**
 * 自定义上传，customRequest为必传
 */
interface Props extends UploadProps {
  /** 单个文件大小：单位为M */ maxSize?: number;
  /** 文件单位大小：默认按1000计算 */ unitSize?: number;
  showTips?: boolean;
  uploadTips?: ITipsGroupItem[];
  /** 上传按钮文本 */
  uploadText?: string;
  /** 上传按钮图标 */
  uploadIcon?: React.ReactNode;
  uploadStyle?: React.CSSProperties;
  /** 过滤禁止的文件后缀列表 */ filterAccept?: string[];
  uploadCallback?: (
    file: UploadFile,
    files: UploadFile[],
    origFile?: UploadFile[],
  ) => void;

  historyFilesChange?: (file: UploadFile) => void;
  historyFiles?: IHistoryFiles[];
  /** 上传出错后是否删除 */ errorRemove?: boolean;
  /** 图片宽高比例,[0,0]表示不限 */
  imgScale?: number[];
  /** 图片宽[0,0]表示在该范围 */
  imgWidth?: number[] | number;
  /** 图片高[0,0]表示在该范围 */
  imgHeight?: number[] | number;
  direction?: IDirection;
  /** 特殊定制上传，按钮和预览图片都是16：9显示 */
  isSpecial?: boolean;
  className?: string;
}

export const Uploader: React.FC<Props> = (props) => {
  const {
    customRequest,
    maxSize = 300,
    unitSize = 1000,
    /** 限制上传数量。当为 1 时，始终用最新上传的文件代替当前文件 */
    maxCount = 1,
    showTips = true,
    uploadTips,
    filterAccept = DefFilterAccept,
    accept,
    fileList,
    uploadCallback,
    historyFilesChange,
    historyFiles = [],
    errorRemove,
    imgScale = [],
    imgWidth,
    imgHeight,
    direction,
    disabled,
    uploadText = '选择文件',
    uploadIcon = <UploadOutlined />,
    uploadStyle,
    isSpecial,
    className,
    ...rest
  } = props;

  const [loading, setLoading] = useState(false);
  const [fileListNew, setFileListNew] = useState<UploadFile[]>([]);
  const [previewOpen, setPreviewOpen] = useState(false);
  const [previewImage, setPreviewImage] = useState('');
  const [previewTitle, setPreviewTitle] = useState('');

  const result = formatByte(maxSize * unitSize * unitSize, 2, {
    dividend: unitSize,
  });
  const maxSizeStr = `${result.value} ${result.unit}`;

  const tagHandleChange = (tag: string) => {
    if (tag) {
      // 拼装成与上传文件对象一样的结果
      const fileItem: UploadFile = {
        uid: tag,
        name: tag,
        fileName: tag,
        url: tag,
      };

      if (maxCount <= 1) {
        setFileListNew([fileItem]);
      } else {
        setFileListNew((ls) => [...ls, ...[fileItem]]);
      }

      if (historyFilesChange) {
        historyFilesChange(fileItem);
      }
    }
  };

  /** 上传出错后操作 */
  const errorChange = useCallback(
    (file: UploadFile, msg: string = '') => {
      setLoading(false);
      Message.error(`上传失败！${msg}`);

      if (errorRemove) {
        setFileListNew((preState) => {
          return preState.filter(
            (v) => v.uid !== file.uid || v.name !== file.name,
          );
        });
      }
    },
    [errorRemove],
  );

  // 上传文件之前的钩子 - 其中fileDateList是已上传的文件列表数据
  const beforeUpload = useCallback(
    (file: UploadFile, files: UploadFile[]) => {
      setLoading(true);
      const fileSuffix = getFileExt(file);

      // todo 这里要重新修复文件类型校验
      if (accept && accept !== '*') {
        let validFile = true;
        const fileTypeList = accept?.split(/\s*,\s*/).map((v) => {
          // 如果没有斜杠，则统一处理成带'.'的文件后缀，一般走普遍校验就行
          if (accept.indexOf('/') === -1 && v.indexOf('.') === -1) {
            v = `.${v}`;
          }

          return v;
        });

        let fileTypeStr = fileTypeList.join('|');
        let reg = new RegExp(fileTypeStr, 'i');

        // 按Media校验或按文件后缀校验
        if (file?.type) {
          validFile = reg.test(file?.type) || reg.test(`.${fileSuffix}`);
        }

        if (!validFile) {
          setLoading(false);
          Message.error(`文件格式不正确，请重新上传`);
          return Upload.LIST_IGNORE;
        }
      }

      let sameList = fileListNew.filter(
        (item: UploadFile) =>
          item.name === file.name || item.lastModified === file.lastModified,
      );

      if (filterAccept?.length && filterAccept.includes(fileSuffix)) {
        Message.error(`${fileSuffix}类型文件禁止上传`);
        setLoading(false);
        return Upload.LIST_IGNORE;
      }

      if (sameList.length >= 1) {
        Message.error(`请勿重复上传同一文件`);
        setLoading(false);
        return Upload.LIST_IGNORE;
      }

      // if (maxCount && fileListNew?.length >= maxCount) {
      //   setLoading(false);
      //   Message.error(`文件数量最多上传${maxCount}个，请先移除已上传文件`);
      //   return Upload.LIST_IGNORE;
      // }

      if (maxSize && file?.size && file.size / unitSize / unitSize > maxSize) {
        setLoading(false);
        // 这里文件大小单位要与运维同步，按1000计算
        Message.error(`文件过大，请重新选择${maxSizeStr}以内的文件`);
        return Upload.LIST_IGNORE;
      }

      if (checkIsImage(file) && imgWidth && imgHeight) {
        const isSize = checkImageSize(file, imgWidth, imgHeight, imgScale).then(
          () => {
            setLoading(false);
            setFileListNew((ls) => [...ls, ...files]);
            return file;
          },
          () => {
            setLoading(false);
            return Upload.LIST_IGNORE;
          },
        );
        return isSize;
      }

      setFileListNew((ls) => [...ls, ...files]);
    },
    [
      fileListNew,
      maxSize,
      filterAccept,
      accept,
      unitSize,
      maxSizeStr,
      imgWidth,
      imgHeight,
      imgScale,
    ],
  );

  // onChange上传文件状态变化，有三个状态status：uploading|done|error。
  // beforeUpload拦截的文件没有这些status
  // 所以在onChange中将拦截的文件进行处理，一般来说，上传的文件要么走到error，要么走到done
  // 进入error状态，代表接口报错了；走到done状态代表了上传成功状态。
  const uploadChange: UploadProps['onChange'] = useCallback(
    (info: UploadChangeParam<UploadFile>) => {
      const { file, fileList: newFileList } = info;

      setFileListNew(newFileList);

      if (file.status === 'uploading') {
        setLoading(true);
        return;
      }

      if (file.status === 'done') {
        const { response } = file;

        // 后端没有返回success，用retcode代替，!response.success
        if (response.retcode !== 0) {
          errorChange(file, response.message);

          return;
        }

        setLoading(false);
        Message.success(`上传成功！`);

        let fileArr = newFileList.map((v) => {
          const fileItem = v?.name;

          // 拼装成与上传文件对象一样的结果
          const fileObj: UploadFile = {
            uid: fileItem,
            name: fileItem,
            fileName: fileItem,
            url: fileItem,
          };

          return fileObj;
        });

        const fileItem = file?.name;

        // 拼装成与上传文件对象一样的结果
        const fileObj: UploadFile = {
          uid: fileItem,
          name: fileItem,
          fileName: fileItem,
          url: fileItem,
        };

        if (uploadCallback) {
          uploadCallback(fileObj, fileArr, newFileList);
        }
      } else if (file.status === 'error') {
        errorChange(file);
      }
    },
    [uploadCallback, errorChange],
  );

  // 删除
  const removeChange = useCallback(
    (file: UploadFile) => {
      setLoading(false);
      if (rest?.onRemove) {
        setFileListNew((preState) => {
          return preState.filter(
            (v) => v.uid !== file.uid || v.name !== file.name,
          );
        });

        rest.onRemove(file);

        // 拼装成与上传文件对象一样的结果
        const fileItem = '';
        const fileObj: UploadFile = {
          uid: fileItem,
          name: fileItem,
          fileName: fileItem,
          url: fileItem,
        };

        if (uploadCallback) {
          uploadCallback(fileObj, [fileObj]);
        }
      }
    },
    [rest, uploadCallback],
  );

  /** 关闭预览图 */
  const handleCancel = () => setPreviewOpen(false);
  /** 打开预览图 */
  const handlePreview = async (file: UploadFile) => {
    if (file) {
      if (!file?.url && !file?.preview) {
        file.preview = await getFileBase64(file?.originFileObj as RcFile);
      }

      setPreviewImage(file?.url || (file?.preview as string));
      setPreviewOpen(true);
      setPreviewTitle(
        file?.name || file?.url!.substring(file?.url!.lastIndexOf('/') + 1),
      );
    }
  };

  const isDisabled = useMemo(() => {
    return (
      loading || disabled || (maxCount > 1 && fileListNew.length >= maxCount)
    );
  }, [loading, disabled, fileListNew, maxCount]);

  useEffect(() => {
    if (fileList) {
      setFileListNew(fileList as any);
    }
  }, [fileList]);

  /** 渲染上传按钮，超数量自动消失按钮 */
  const uploadButton = (
    <Button
      disabled={isDisabled}
      type={rest?.listType === 'picture-card' ? 'default' : 'primary'}
      icon={uploadIcon}
      loading={loading}
      className="uploader-btn"
      style={uploadStyle}>
      <span
        style={{
          display: rest?.listType === 'picture-card' ? 'block' : 'inline',
        }}>
        {loading ? `上传中...` : uploadText}
      </span>
    </Button>
  );

  return (
    <UploaderRender>
      <Upload
        {...rest}
        disabled={isDisabled}
        maxCount={maxCount}
        name="file"
        customRequest={customRequest}
        accept={accept ?? '*'}
        fileList={fileListNew}
        beforeUpload={beforeUpload as any}
        onChange={uploadChange}
        onPreview={handlePreview}
        itemRender={(originNode: ReactElement, file: UploadFile) => {
          return (
            <UploaderListItem
              key={file.name}
              file={file}
              removeChange={() => removeChange(file)}
              previewChange={() => handlePreview(file)}
            />
          );
        }}
        className={classNames(`custom-uploader`, className, {
          'is-horizontal': direction === 'horizontal',
          'custom-uploader-special': isSpecial,
        })}>
        {uploadButton}
      </Upload>

      {historyFiles?.length > 0 && (
        <UploaderHistory
          disabled={isDisabled}
          selectedList={fileListNew}
          dataList={[...historyFiles]}
          onChange={tagHandleChange}
        />
      )}

      {showTips && (
        <UploaderTips
          tips={
            uploadTips ?? [
              {
                title: '文件规范',
                list: [
                  `文件大小：单个文件大小≤${maxSizeStr}`,
                  `文件数量：最多支持上传${maxCount}个附件`,
                ],
              },
            ]
          }
          isSimple
        />
      )}

      {previewOpen && (
        <UploaderPreview
          title={previewTitle}
          onCancel={handleCancel}
          previewImage={previewImage}
        />
      )}
    </UploaderRender>
  );
};
